package xyz.cerberu.cet.user.controller;

import org.apache.http.HttpStatus;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresGuest;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import xyz.cerberu.cet.global.domain.ExceptionEncode;
import xyz.cerberu.cet.global.domain.Result;
import xyz.cerberu.cet.global.utils.JSR303Utils;
import xyz.cerberu.cet.global.utils.ResultUtils;
import xyz.cerberu.cet.global.utils.StringUtils;
import xyz.cerberu.cet.user.domain.User;
import xyz.cerberu.cet.user.exception.EmailException;
import xyz.cerberu.cet.user.exception.UserBanAccountException;
import xyz.cerberu.cet.user.service.UserServiceImpl;

import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;

/**
 * @author β世界
 * Created on 16:52
 * @Description 用户相关的控制器
 */
@Controller
@RequestMapping("/user")
public class UserController {
    /** 是否记住我 **/
    private final static String REMEMBER = "on";
    /** 注册的邮件类型 **/
    private final static int REGISTER = 1;
    /** 忘记密码的邮件类型 **/
    private final static int FORGET = 2;
    /** session中验证信息存储的key**/
    private final static String VERIFY_EMAIL = "verifyEmail";
    private final static String VERIFY_TYPE = "verifyType";
    /** session存储验证结果的key**/
    private final static String VERIFY_RESULT = "verifyResult";
    /** 用户服务的基本实现 **/
    private final UserServiceImpl userServiceImpl;

    @Autowired
    public UserController(UserServiceImpl userServiceImpl) {
        this.userServiceImpl = userServiceImpl;
    }

    /**
     * @author β世界 on 22:14 2020/6/14
     * 用户登陆的控制器,
     * 注意：只有用户没有登录的时候才可以访问
     * @param user: 用户管理信息
     **/
    @RequiresGuest
    @ResponseBody
    @RequestMapping(value = "/log",method = RequestMethod.GET)
    public Result<Object> login(@Valid User user, BindingResult bindingResult, String remenber, HttpServletRequest request){
        Subject subject = SecurityUtils.getSubject();
        Result<Object> result;

        // 处理数据校验的内容
        if(bindingResult.hasErrors()){
            String message = JSR303Utils.FieldMessage(bindingResult,"username");
            if(!"".equals(message)){
                return ResultUtils.genErrorResult(HttpStatus.SC_UNAUTHORIZED, message);
            }
        }

        // 将用户信息封装成token对象
        UsernamePasswordToken token = new UsernamePasswordToken(user.getEmail(),user.getPassword());

        try{
            // 是否记住登陆
            if(REMEMBER.equals(remenber)){
                token.setRememberMe(true);
            }
            subject.login(token);
            // 封装用户的基本信息到前端
            Map<String,Object> map = new HashMap<>();
            // 添加头像
            map.put("photo","/CeT/img/Logo.jpg");
            // 添加用户的基本信息
            map.put("username",((User)subject.getPrincipal()).getUsername());
            result = ResultUtils.genSuccessResult(map.toString());
        }catch (AuthenticationException e){ // 这个异常的处理也可以通过ExceptionHandler来处理(懒了，暂时就这样吧)
            if(e instanceof UnknownAccountException){
                result = ResultUtils.genErrorResult(HttpStatus.SC_UNAUTHORIZED,"用户不存在");
            }else if(e instanceof UserBanAccountException){
                result = ResultUtils.genErrorResult(HttpStatus.SC_FORBIDDEN,"用户被封禁");
            }else if(e instanceof IncorrectCredentialsException){
                result = ResultUtils.genErrorResult(HttpStatus.SC_UNAUTHORIZED,"邮箱或者密码错误");
            }else {
                result = ResultUtils.genErrorResult(HttpStatus.SC_INTERNAL_SERVER_ERROR,"服务器异常,登陆失败");
            }
            e.printStackTrace();
            token.clear();
        }

        return result;
    }
    
    /**
     * @author β世界 on 22:25 2020/6/14
     * 用户注册的控制器。
     * @param user: 用户信息的实体对象
     * @param bindingResult: 参数校验异常信息
     **/
    @RequiresGuest
    @ResponseBody
    @RequestMapping(value = "/reg",method = RequestMethod.POST)
    public Result register(@Valid User user, BindingResult bindingResult, HttpSession session, HttpServletRequest request){
        // 检查是否通过验证
        if(session.getAttribute(VERIFY_RESULT) == null || REGISTER != (Integer) session.getAttribute(VERIFY_RESULT)){
            return ResultUtils.genErrorResult(ExceptionEncode.STATUS_EXCEPTION,"请先验证~");
        }
        // 将验证状态取消
        session.removeAttribute(VERIFY_RESULT);
        // 处理数据校验的内容
        if(bindingResult.hasErrors()){
            return ResultUtils.genErrorResult(HttpStatus.SC_UNAUTHORIZED,JSR303Utils.FieldMessage(bindingResult));
        }
        // 检测是否包含特殊字符
        if(StringUtils.legal(user.getUsername()) || "".equals(user.getUsername().trim())){
            return ResultUtils.genErrorResult(HttpStatus.SC_UNAUTHORIZED, "输入内容包含特殊-和_以外的特殊字符");
        }

        switch (userServiceImpl.register(user)){
            // 注册失败
            case 0:
                return ResultUtils.genErrorResult(HttpStatus.SC_UNAUTHORIZED,"注册失败,邮箱已被使用");
            case 1:
                return ResultUtils.genSuccessResult("注册成功");
            default:
                return ResultUtils.genErrorResult(HttpStatus.SC_INTERNAL_SERVER_ERROR,"服务器出错");
        }

    }

    /**
     * @author β世界 on 9:26 2020/11/6
     * 忘记密码接口 (必须是非登录状态下才能访问)
     * 用户先通过一次邮箱验证，来确定这个邮箱可以修改密码
     * 通过邮箱验证之后才可以进行修改
     * @param newPassword: 新的密码
     * @return com.Cet.common.domain.Result
     **/
    @RequiresGuest
    @ResponseBody
    @RequestMapping(value = "/forget",method = RequestMethod.POST)
    public Result forget(String email,String newPassword,HttpSession session){
        // 检查是否通过验证
        if(session.getAttribute(VERIFY_RESULT) == null || FORGET != (Integer) session.getAttribute(VERIFY_RESULT)){
            return ResultUtils.genErrorResult(ExceptionEncode.STATUS_EXCEPTION,"请先验证~");
        }
        // 将验证状态取消
        session.removeAttribute(VERIFY_RESULT);

        // 检查需要修改的邮箱是否和发送验证的邮箱一致
        String verifyEmail = (String) session.getAttribute(VERIFY_EMAIL);
        if(!email.equals(verifyEmail)){
            return ResultUtils.genErrorResult(ExceptionEncode.STATUS_EXCEPTION,"状态错误");
        }
        // 封装信息
        User user = userServiceImpl.findUser(email);
        user.setPassword(newPassword);
        // 提交修改
        if(userServiceImpl.updatePassword(user) < 1){
            return ResultUtils.genErrorResult(ExceptionEncode.EMAIL_EXCEPTION,"更新密码失败");
        }

        return ResultUtils.genSuccessResult("更新成功");
    }

    /**
     * @author β世界 on 19:19 2020/7/1
     * 发送验证码
     * @param email: 邮箱
     * @param type: 验证码类型
     **/
    @ResponseBody
    @RequestMapping(value = "/verify",method = RequestMethod.POST)
    public Result<Object> sendVerifyCode(String email, @RequestParam(defaultValue = "0") Integer type, HttpSession session){
        // 检测邮件类型
        if(type > FORGET || type < REGISTER){
            throw new IllegalStateException();
        }
        try {
            // 检查邮箱是否是空和是否符合
            if(!StringUtils.isEmpty(email) && StringUtils.isEmail(email)){
                userServiceImpl.verifyEmail(email,type);
                // 记录当前发送验证码的邮箱和验证类型
                session.setAttribute(VERIFY_TYPE,type);
                session.setAttribute(VERIFY_EMAIL,email);
            }else {
                return ResultUtils.genErrorResult(ExceptionEncode.EMAIL_EXCEPTION,"邮件填写错误，请检查邮箱格式");
            }
        } catch (Exception e) {
            e.printStackTrace();
            if(e instanceof MessagingException){
                return ResultUtils.genErrorResult(ExceptionEncode.CODE_EXCEPTION,"服务器错误，邮件发送失败");
            }else if(e instanceof EmailException){
                return ResultUtils.genErrorResult(ExceptionEncode.EMAIL_EXCEPTION,e.getMessage());
            }
        }
        return ResultUtils.genSuccessResult("邮件发送成功");
    }

    /**
     * @author β世界 on 22:16 2020/11/8
     * 验证用户验证码
     * @param code: 邮箱验证码
     * @return com.Cet.common.domain.Result
     **/
    @ResponseBody
    @RequestMapping(value = "/verifyCode",method = RequestMethod.POST)
    public Result<Object> verifyEmailCode(String email,@RequestParam(defaultValue = "") String code, HttpSession session){
        // 注册验证码错误
        Integer type = (Integer) session.getAttribute(VERIFY_TYPE);
        // 检测邮箱格式是否正确
        if(StringUtils.isEmpty(email) && !StringUtils.isEmail(email)){
            return ResultUtils.genErrorResult(ExceptionEncode.EMAIL_EXCEPTION,"邮件填写错误，请检查邮箱格式");
        }
        // 检测状态
        if(type == null) {
            return ResultUtils.genErrorResult(ExceptionEncode.STATUS_EXCEPTION, "状态异常");
        }
        // 拿邮箱去获取验证码来对比
        if(!userServiceImpl.checkCode(session.getAttribute(VERIFY_EMAIL).toString(),code,type)){
            return ResultUtils.genErrorResult(HttpStatus.SC_UNAUTHORIZED,"验证码错误");
        }
        // 验证成功添加验证结果记录
        session.setAttribute(VERIFY_RESULT,type);
        return ResultUtils.genSuccessResult("OK");
    }

    /**
     * @author β世界 on 12:50 2020/6/20
     * 用户登出服务,用户必须是登陆状态
     **/
    @ResponseBody
    @RequestMapping(value = "/logout")
    public Result<Object> logout(){
        Subject subject = SecurityUtils.getSubject();
        if(subject != null){
            // 终止用户的会话
            subject.getSession().stop();
            // 执行登出语句
            subject.logout();
        }else{
            return ResultUtils.genErrorResult(ExceptionEncode.USER_STATUS_EXCEPTION,"用户状态异常");
        }
        return ResultUtils.genSuccessResult("OK");
    }

}
